﻿using System;
using System.IO;
using System.Text;

namespace SuiHan.Commons.Logging {
    /// <summary>
    /// 日志文件输出
    /// </summary>
    public class LogFileWriter {
        private static readonly string TAG = typeof(LogFileWriter).FullName;

        private string _fileFormat =
            AppDomain.CurrentDomain.SetupInformation.ApplicationName.Replace(".exe", string.Empty);

        private string _outputDir = Path.Combine(AppDomain.CurrentDomain.SetupInformation.ApplicationBase, "log");
        private readonly byte[] newLine = Encoding.UTF8.GetBytes("\n");

        /// <summary>
        /// 输出日志文件名,默认与启动exe程序的名称相同
        /// </summary>
        public string FileFormat {
            get => _fileFormat;
            set {
                if (string.IsNullOrEmpty(value)) return;
                _fileFormat = value;
            }
        }

        /// <summary>
        /// 日志文件输出目录,默认在启动exe程序同级目录的logs文件夹下
        /// </summary>
        public string OutputDir {
            get => _outputDir;
            set {
                if (string.IsNullOrEmpty(value)) return;
                _outputDir = value;
            }
        }

        /// <summary>
        /// 单个日志文件最大长度(Default:10mb)
        /// </summary>
        public long MaxLength { get; set; } = 10485760;

        private FileStream _fileStream;
        private long currentLength;
        private FileInfo currentFile;
        private readonly LoggerConfig config;

        public LogFileWriter(LoggerConfig config) {
            this.config = config;
            if (!string.IsNullOrEmpty(config.OutDir)) OutputDir = config.OutDir;
            if (!string.IsNullOrEmpty(config.FileFormat)) FileFormat = config.FileFormat;
            if (config.SigleFileMaxLength > 0) MaxLength = config.SigleFileMaxLength;

            new LoggerCleanThread(FileFormat, OutputDir).Start();
        }

        private int GetIndex() {
            DirectoryInfo outputFolder = new(OutputDir);
            FileInfo[] files = outputFolder.GetFiles($"{FileFormat}-*.log", SearchOption.TopDirectoryOnly);
            int maxIndex = 1;
            if (files.Length == 0) {
                return maxIndex;
            }

            foreach (FileInfo file in files) {
                string indexStr = file.Name.Replace(".log", "").Substring(file.Name.LastIndexOf('-') + 1);
                if (int.TryParse(indexStr, out int index)) {
                    if (index > maxIndex) {
                        maxIndex = index;
                    }
                }
            }

            return maxIndex + 1;
        }

        private void OpenFile() {
            if (currentFile != null) return;
            if (!Directory.Exists(OutputDir)) {
                Directory.CreateDirectory(OutputDir);
            }

            string filepath = Path.Combine(OutputDir, $"{FileFormat}.log");
            FileInfo file = new(filepath);
            currentLength = file.Exists ? file.Length : 0;
            currentFile = file;
            _fileStream = new FileStream(filepath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
        }

        private void OpenNext() {
            _fileStream.Close();
            currentFile.MoveTo(Path.Combine(OutputDir, $"{FileFormat}-{GetIndex()}.log"));
            _fileStream = null;
            currentFile = null;
            currentLength = 0;
            OpenFile();
        }

        public void Write(string content) {
            try {
                // Console.WriteLine($"stream: {_fileStream == null}, content: {content == null}, currentFile:{currentFile == null}");
                // Console.WriteLine("");

                byte[] buffer = Encoding.UTF8.GetBytes(content);
                int writeLength = buffer.Length;
                OpenFile();
                if (currentLength + writeLength > MaxLength) {
                    OpenNext();
                }

                _fileStream.Write(buffer, 0, writeLength);
                _fileStream.Write(newLine, 0, newLine.Length);
                _fileStream.Flush();
                currentLength += writeLength;
            } catch (Exception e) {
                Console.WriteLine($"{DateTime.Now.ToString(config.DateFormat)} {TAG} 日志发生错误 {e}");
                if (config.ConsoleEnabled is false) {
                    Console.WriteLine(content);
                }
            }
        }

        ~LogFileWriter() {
            _fileStream.Close();
        }
    }
}
